﻿using System;
using System.Linq;
using System.Linq.Expressions;
using MathService.Vectors;

namespace MathService.Interpolation
{
    public class Lagrange : IInterpolator
    {
        /* ------------------------------------------------------------------------------------------ */

        private delegate void Initializator(double[] X, double[] Y);

        /* ------------------------------------------------------------------------------------------ */

        public static Polynom GetPolynom(double[] X, double[] Y)
        {
            return Y.Select((y, i) => new { i, y, p = Polynom.Roots(X.Where((x, j) => j != i)) }).Select(v => (v.y / v.p.Value(X[v.i])) * v.p).Sum();
        }

        /* ------------------------------------------------------------------------------------------ */

        public event EventHandler OnInitialized;

        private void Invoke_OnInitialized(EventArgs Args)
        {
            var lv_Handler = OnInitialized;
            if(lv_Handler != null)
                lv_Handler(this, Args);
        }
        private void Invoke_OnInitialized() { Invoke_OnInitialized(EventArgs.Empty); }

        /* ------------------------------------------------------------------------------------------ */

        private Polynom f_Polynom;
        private readonly IAsyncResult f_InitializationResult;

        /* ------------------------------------------------------------------------------------------ */

        public Polynom P { get { return f_Polynom; } set { f_Polynom = value; } }
        public bool Initialized { get { return f_InitializationResult.IsCompleted; } }

        public double this[double x] { get { return Value(x); } }

        /* ------------------------------------------------------------------------------------------ */

        public Lagrange(double[] X, double[] Y)
        {
            f_InitializationResult = ((Initializator)Initialize).BeginInvoke(X, Y, OnInitializationComplite, null);
        }

        public Lagrange(params Vector2D[] P)
        {
            var x = new double[P.Length];
            var y = new double[P.Length];
            for(var i = 0; i < P.Length; i++) { x[i] = P[i].X; y[i] = P[i].Y; }
            f_InitializationResult = ((Initializator)Initialize).BeginInvoke(x, y, OnInitializationComplite, null);
        }

        /* ------------------------------------------------------------------------------------------ */

        private void Initialize(double[] X, double[] Y) { f_Polynom = GetPolynom(X, Y); }

        private void OnInitializationComplite(IAsyncResult Result) { Invoke_OnInitialized(); }

        public double Value(double x)
        {
            if(!Initialized) f_InitializationResult.AsyncWaitHandle.WaitOne();
            return f_Polynom.Value(x);
        }

        public Func<double, double> GetFunction() { return Value; }

        public Expression<Func<double, double>> GetExpression() { return f_Polynom.GetExpression(); }

        /* ------------------------------------------------------------------------------------------ */
    }
}
